package com.ljm.boot.helloword.simpleStudy.springBoot.context;

import com.ljm.boot.helloword.simpleStudy.springBoot.annotation.*;
import com.ljm.boot.helloword.simpleStudy.springBoot.bean.BeanDefinition;
import com.ljm.boot.helloword.simpleStudy.springBoot.interfaces.BeanFactoryPostProcessor;
import com.ljm.boot.helloword.simpleStudy.springBoot.interfaces.BeanPostProcessor;
import com.ljm.boot.helloword.simpleStudy.springBoot.interfaces.InitializitingBean;

import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

;

public class ThrobApplicationContext {

    /**
     * Spring的生命周期
     * 1、实例化：直接new一个对象（无参构造方法）
     *     1.1、实例化前postProcessBeanFactory这个机制允许我们在实例化相应对象之前对注册到容器中的BeanDefinition的存储信息进行修改
     * 2、属性填充：依赖注入
     *    2.1 实现各项环绕接口(BeanNameAware、BeanFactoryAware、ApplicationContextAware)
     *    3.0、初始化前 BeanPostProcessorBefore方法
     * 3、初始化：运行指定的init-method
     *     3.2、初始化前 BeanPostProcessorAfter方法 aop等，之后将放入单例池Map2
     * 4、使用
     * 5、销毁
     */

    private Class configClass;
    private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<String, BeanDefinition>();
    private Map<String,Object> singletonObjects = new HashMap<String, Object>();
    private List<BeanFactoryPostProcessor> beanFactoryPostProcessorList = new ArrayList<BeanFactoryPostProcessor>();

    private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<BeanPostProcessor>();

    public ThrobApplicationContext(Class configClass) {
        this.configClass = configClass;
        scan(configClass);
        preInstantiateSingletons();//实例化单例Bean
    }
    //bean类-->无参构造方法 -->对象-->依赖注入-->初始化前(@PostConstruct)-->初始化后-->放入单例池Map-->bean

    public void scan(Class configClass){
        ComponentScan annotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
        String path = annotation.value();
        path = path.replace(".","/");
        ClassLoader classLoader = ThrobApplicationContext.class.getClassLoader();
        URL resource = classLoader.getResource(path);
        File file = new File(resource.getFile());
        File[] files =  file.listFiles();
        for (File f:files) {
            String fileName = f.getAbsolutePath();
            if(!f.exists()) continue;
            if(!fileName.endsWith(".class")) continue;
            fileName = fileName.substring(fileName.indexOf("com"),fileName.indexOf(".class"));
            fileName = fileName.replace("\\",".");
            try {
                Class clazz = classLoader.loadClass(fileName);
                if(clazz.isAnnotationPresent(Component.class)){
                    if(BeanPostProcessor.class.isAssignableFrom(clazz)){
                        BeanPostProcessor beanPostProcessor = (BeanPostProcessor) clazz.newInstance();
                        beanPostProcessorList.add(beanPostProcessor);
                    }
                    Component component = (Component) clazz.getAnnotation(Component.class);
                    BeanDefinition beanDefinition =  new BeanDefinition();
                    String beanName = component.value();
                    if(clazz.isAnnotationPresent(Scope.class)){
                        Scope scope = (Scope) clazz.getAnnotation(Scope.class);
                        beanDefinition.setScope(scope.value());
                    }else{
                        beanDefinition.setScope("singleton");
                    }
                    beanDefinition.setClazz(clazz);
                    beanDefinitionMap.put(beanName,beanDefinition);
                    System.out.println(clazz);
                }
            }catch (Exception e){
                e.printStackTrace();
            }
        }

    }
/**
 *
    protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {

        // 调用BeanNameAware,BeanClassLoaderAware,BeanFactoryAware的set方法
        invokeAwareMethods(beanName, bean);

        // 调用BeanPostProcessor.postProcessBeforeInitialization方法
        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);

        // 调用InitializingBean/init-method方法
        invokeInitMethods()

        // 调用BeanPostProcessor.postProcessAfterInitialization方法
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);

    }
 */

    private Object createBean(String beanName,BeanDefinition beanDefinition){
        try{
            Class clazz = beanDefinition.getClazz();
            Object instance = clazz.newInstance();

            //对象进行依赖注入
            for (Field field :clazz.getDeclaredFields()){
                if(field.isAnnotationPresent(Autowired.class)){
                    Object bean = field.getName();
                    if(bean == null )throw new NullPointerException();
                    field.setAccessible(true);
                    field.set(instance,getBean(field.getName()));
                }
            }
            //实例化
            for (BeanFactoryPostProcessor beanFactoryPostProcessor:beanFactoryPostProcessorList) {
                beanFactoryPostProcessor.postProcessBeanFactory(null);
            }

            //初始化前 PostConstruct
            for (BeanPostProcessor beanPostProcessor:beanPostProcessorList) {
                instance = beanPostProcessor.BeanPostProcessorBeforeInitialization(instance,beanName);
            }

            //给属性初始化
            if(instance instanceof InitializitingBean){
                ((InitializitingBean) instance).afterPropertiesSet();
            }

            //初始化后
            for (BeanPostProcessor beanPostProcessor:beanPostProcessorList) {
                instance = beanPostProcessor.BeanPostProcessorAfterInitialization(instance,beanName);
            }


            return instance;
        } catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

    public Object getBean(String beanName){
        if(!beanDefinitionMap.containsKey(beanName)) throw new NullPointerException();
        BeanDefinition beanDefinition =   beanDefinitionMap.get(beanName);
        if(beanDefinition.getScope().equals("singleton")){
            Object  obj =  singletonObjects.get(beanName);
            return obj;
        }else{
            beanDefinition.setScope("prototype");
            Object  obj = createBean(beanName,beanDefinition);
            return obj;
        }
    }

    public void  preInstantiateSingletons(){
        for (String beanName: beanDefinitionMap.keySet()) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            if(beanDefinition.getScope().equals("singleton")){
                Object  obj = createBean(beanName,beanDefinition);
                singletonObjects.put(beanName,obj);
            }
        }
    }
}
